QuteBrowser scrollcommands 滚动命令实现原理
介绍
QuteBrowser 表面上是一个 Vim 风格的浏览器,内部则是一套类编辑器(Vim、Emacs)的命令模式。各个快捷键背后都是命令(函数),在运行时由用户来触发。QuteBrowser 使用 Python 编写,使得我有机会一探究竟。本文介绍与滚动相关命令的实现。
对应的文件路径是:qutebrowser\components\scrollcommands.py
注册命令
只要给函数添加注解,就能向系统中注册任务:
@cmdutils.register()
@cmdutils.argument('tab', value=cmdutils.Value.cur_tab)
@cmdutils.argument('count', value=cmdutils.Value.count)
def scroll_px(tab: apitypes.Tab, dx: int, dy: int, count: int = 1) -> None:
# ……
其中:
@cmdutils.register
负责注册命令@cmdutils.argument
负责绑定相关参数
所有注册指令
按 cmdutils.register
关键词全局搜索,这一快速找出 QuteBrowser 中所有的命令。
按像素滚动
具体实现如下:
@cmdutils.register()
@cmdutils.argument('tab', value=cmdutils.Value.cur_tab)
@cmdutils.argument('count', value=cmdutils.Value.count)
def scroll_px(tab: apitypes.Tab, dx: int, dy: int, count: int = 1) -> None:
"""Scroll the current tab by 'count * dx/dy' pixels.
Args:
dx: How much to scroll in x-direction.
dy: How much to scroll in y-direction.
count: multiplier
"""
dx *= count
dy *= count
cmdutils.check_overflow(dx, 'int')
cmdutils.check_overflow(dy, 'int')
tab.scroller.delta(dx, dy)
其中:
- tab 参数的类型:
- 对于交互命令是
cmdutils.Value.cur_tab
- 对于函数参数是
apitypes.Tab
- 对于交互命令是
- count 的类型
cmdutils.Value.count
看起来只是表示一个数字。
在代码实现中,经过计算和边界检查,最终调用 tab.scroller.delta(dx, dy)
完成具体 UI 操作。其中:
- tab 类型为
apitypes.Tab
,这就是 Tab 对应的 Widget。 tab.scroller
就是对应页面负责滚动的类。
滚动到锚点
再看一个滚动到锚点的命令:
@cmdutils.register()
@cmdutils.argument('tab', value=cmdutils.Value.cur_tab)
def scroll_to_anchor(tab: apitypes.Tab, name: str) -> None:
"""Scroll to the given anchor in the document.
Args:
name: The anchor to scroll to.
"""
tab.scroller.before_jump_requested.emit()
tab.scroller.to_anchor(name)
其中:
before_jump_requested
信号的作用,是告知 scroller 在跳转前记录当前位置,以便还能跳回来to_anchor
则是实际进行锚点跳转
缺失的参数呢?
在两个函数中可以看到,都有部分参数没有给定:
scroll_to_anchor
的 namescroll_px
的 dx 和 dy
这些参数都没有通过 @cmdutils.argument
注册,想必是不受用户控制的。
以 scroll_px 为例,把程序运行起来实验:
- 在交互式命令中能够找到
scroll_px
- 进行交互式调用,会提示我:the following arguments are required: dx, dy
从中可以看出,所有的注册过的命令都可以交互使用,但不是所有的命令在交互都支持交互使用。